package com.example.deepschool.service.User;

import com.example.deepschool.Model.Response.ResponseData;
import com.example.deepschool.enums.GlobalVariables_String;
import com.example.deepschool.exception.CustomException;
import com.example.deepschool.repositories.UserRepository;
import com.example.deepschool.util.*;
import lombok.SneakyThrows;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.UUID;


/**
 * 用户服务接口的实现类，提供用户注册、登录、更新信息等操作。
 */
@Service
public class UserImpl implements User {
	
	private final UserRepository userRepository;
	
	/**
	 * 用户服务构造函数，初始化用户仓库。
	 *
	 * @param userRepository 用户信息仓库的接口实例。
	 */
	public UserImpl (UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	
	/**
	 * 根据用户信息生成新的会话。
	 *
	 * @param user 用户信息。
	 * @return 经过处理的用户信息，包括生成的会话ID。
	 * @throws RuntimeException 如果用户不存在或已在其他设备登录。
	 */
	private com.example.deepschool.Model.User getUser (com.example.deepschool.Model.User user) {
		if (Redis.EXISTS(user.getUsername())) {
			throw new RuntimeException("已在另一台设备登录.");
		}
		Long permission = user.getPermissions();
		if (! PermissionsControl.VERIFY_LOGIN_REGISTER(permission)) {
			System.out.println("用户已被禁用");
			throw new RuntimeException("用户已被禁用");
		}
		
		// 生成并设置新的会话ID，将用户信息加密存储
		String token = UUID.randomUUID().toString();
		String username = user.getUsername();
		Redis.SET(token, username, 60 * 30);
		Redis.HSET(username, "user", user.toJson());
		Redis.HSET(username, "UUID", token);
		Redis.EXPIRE(username, 60 * 30);
		user.setPassword(null);
		return user;
	}
	
	/**
	 * 更新用户信息并刷新会话。
	 *
	 * @param user 用户信息。
	 * @param UUID 当前会话ID。
	 * @return 更新后的用户信息。
	 * @throws RuntimeException 如果更新失败。
	 */
	private com.example.deepschool.Model.User getUser (com.example.deepschool.Model.User user, String UUID) {
		// 更新用户信息并验证更新是否成功
		try {
			if (this.userRepository.update(user) > 0) {
				Redis.HSET(user.getUsername(), "user", user.toJson());
				Redis.EXPIRE(UUID, 60 * 30);
				Redis.EXPIRE(user.getUsername(), 60 * 30);
				return user;
			}
			throw new PersistenceException("信息更新失败");
		} catch (Exception e) {
			throw new RuntimeException("服务器异常");
		}
		
		
	}
	
	/**
	 * 用户注册，包括密码加密和权限设置。
	 *
	 * @param user 待注册的用户信息。
	 */
	@SneakyThrows
	@Override
	public void register (com.example.deepschool.Model.User user) {
		try {
			// 对密码进行SHA256加密
			user.setPassword(SHA256.calculateSha256Hash(user.getPassword()));
			// 设置默认权限
			user.setPermissions(PermissionsControl.RegularPermission());
			this.userRepository.insert(user);
		} catch (DataIntegrityViolationException e) {
			if (e.getCause() instanceof SQLIntegrityConstraintViolationException) {
				throw new CustomException(400, "用户名或邮箱已存在.");
			}
			throw new CustomException(500, "服务器异常");
		} catch (Exception e) {
			throw new CustomException(500, "服务器异常");
		}
	}
	
	
	/**
	 * 通过用户名登录，验证用户身份并生成新的会话。
	 *
	 * @param user 用户信息，包含用户名和密码。
	 * @return 经过验证和处理的用户信息。
	 */
	@SneakyThrows
	@Override
	public com.example.deepschool.Model.User login_username (com.example.deepschool.Model.User user) {
		try {
			// 对密码进行SHA256加密
			user.setPassword(SHA256.calculateSha256Hash(user.getPassword()));
			com.example.deepschool.Model.User oldUser = this.userRepository.selectByUsername(user);
			// 检查用户信息是否有效
			if (oldUser == null) {
				throw new RuntimeException("用户名或密码错误.");
			}
			return getUser(oldUser);
		} catch (Exception e) {
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}
	
	/**
	 * 通过邮箱登录，验证用户身份并生成新的会话。
	 *
	 * @param user 用户信息，包含邮箱和密码。
	 * @return 经过验证和处理的用户信息。
	 */
	@SneakyThrows
	@Override
	public com.example.deepschool.Model.User login_email (com.example.deepschool.Model.User user) {
		try {
			// 对密码进行SHA256加密
			user.setPassword(SHA256.calculateSha256Hash(user.getPassword()));
			com.example.deepschool.Model.User oldUser = this.userRepository.selectByEmail(user);
			// 检查用户信息是否有效
			if (oldUser == null) {
				throw new RuntimeException("用户名或密码错误.");
			}
			return getUser(oldUser);
		} catch (Exception e) {
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}
	
	/**
	 * 根据用户ID获取用户信息。
	 *
	 * @param id 用户ID。
	 * @return 用户信息。
	 */
	@SneakyThrows
	@Override
	public com.example.deepschool.Model.User getUserById (long id) {
		try {
			return this.userRepository.select(id);
		} catch (Exception e) {
			throw new CustomException(500, "服务器异常.");
		}
	}
	
	/**
	 * 更新用户信息，主要通过会话ID验证用户身份。
	 *
	 * @param user 更新后的用户信息。
	 * @param UUID 当前有效的会话ID。
	 * @return 更新后的用户信息。
	 */
	@SneakyThrows
	@Override
	public com.example.deepschool.Model.User updateUser (com.example.deepschool.Model.User user, String UUID) {
		try {
			com.example.deepschool.Model.User oldUser = com.example.deepschool.Model.User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			oldUser.setNickname(user.getNickname());
			return getUser(oldUser, UUID);
		} catch (RuntimeException e) {
			throw new CustomException(500, e.getMessage());
		}
	}
	
	/**
	 * 更新用户头像，包括文件校验、存储和数据库信息更新。
	 *
	 * @param file       用户上传的头像文件。
	 * @param fileSHA256 文件的SHA256校验值。
	 * @param UUID       当前有效的会话ID。
	 * @return 更新后的用户信息。
	 */
	@SneakyThrows
	@Override
	public com.example.deepschool.Model.User putAvatar (MultipartFile file, String fileSHA256, String UUID) {
		String Path = null;
		boolean isDelPath = false;
		try {
			com.example.deepschool.Model.User user = com.example.deepschool.Model.User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			String oldAvatar = user.getAvatarUrl();
			user.setAvatarUrl(fileSHA256);
			String type = IO.getFileType(file);
			if (! Verify.VerifyFileType_Image(type)) {
				throw new RuntimeException("文件类型，不允许");
			}
			String SHA_256 = SHA256.calculateFileSHA256(file);
			if (! fileSHA256.equals(SHA_256)) {
				throw new RuntimeException("文件在传输过程中出现异常.");
			}
			Path = GlobalVariables_String.File_Path_Avatar.getValue() + SHA_256;
			String oldAvatarPath = GlobalVariables_String.File_Path_Avatar.getValue() + oldAvatar;
			if (! IO.isFileExist(Path)) {
				IO.writeFile(file, Path);
			}
			if (this.userRepository.selectAvatarCount(oldAvatar) == 1) {
				IO.deleteFile(oldAvatarPath);
			}
			if (this.userRepository.selectAvatarCount(SHA_256) == 0) {
				isDelPath = true;
			}
			return getUser(user, UUID);
			
		} catch (Exception e) {
			if (isDelPath) {
				IO.deleteFile(Path);
			}
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}
	
	/**
	 * 获取用户头像，如果未设置则返回默认头像。
	 *
	 * @param Avatar 用户头像的标识。
	 * @return 包含头像文件和类型的响应体。
	 */
	@SneakyThrows
	@Override
	public ResponseData getAvatar (String Avatar, String range) {
		try {
			long start = 0;
			if (range != null) {
				start = Long.parseLong(range.split("=")[1].split("-")[0]);
			}
			Avatar = ! Avatar.isEmpty() ? Avatar : GlobalVariables_String.Avatar_Default.getValue();
			String path = GlobalVariables_String.File_Path_Avatar.getValue() + Avatar;
			return IO.onlineReadFile(path, start, range);
		} catch (RuntimeException e) {
			throw new CustomException(400, e.getMessage());
		}
	}
	
	/**
	 * 更新用户密码，需要验证旧密码并确认新密码。
	 *
	 * @param oldPassword  用户当前密码。
	 * @param newPassword  用户新密码。
	 * @param newPassword2 新密码的确认。
	 * @param UUID         当前有效的会话ID。
	 */
	@SneakyThrows
	@Override
	public void updatePassword (String oldPassword, String newPassword, String newPassword2, String UUID) {
		com.example.deepschool.Model.User user = com.example.deepschool.Model.User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
		try {
			Verify.VerifyUpdatePassword(SHA256.calculateSha256Hash(oldPassword), newPassword, newPassword2, user);
			user.setPassword(SHA256.calculateSha256Hash(newPassword));
			if (this.userRepository.updatePassword(user) > 0) {
				logout(UUID);
			}
			throw new Exception("服务器异常");
		} catch (Exception e) {
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}
	
	/**
	 * 删除指定ID的用户。
	 *
	 * @param id 待删除用户的ID。
	 * @return 删除操作影响的行数。
	 */
	@SneakyThrows
	@Override
	public int deleteUser (long id) {
		try {
			return this.userRepository.delete(id);
		} catch (Exception e) {
			throw new CustomException(500, "服务器异常.");
		}
	}
	
	/**
	 * 用户登出，清除会话信息。
	 *
	 * @param UUID 当前有效的会话ID。
	 */
	@Override
	public void logout (String UUID) {
		String username = Redis.GET(UUID);
		Redis.DEL(UUID);
		Redis.DEL(username);
	}
}
